#include "netlib/net/socket/buffer.h"

#include <sys/uio.h>

#include <algorithm>
#include <cstdint>

void netlib::net::Buffer::Retrieve(int size) {
	assert(size <= ReadableBytes());
	if (size < ReadableBytes()) {
		read_idx_ += size;
	} else {
		read_idx_ = write_idx_ = kInitReadIndex;
	}
}

void netlib::net::Buffer::Append(const char* data, int len) {
	if (!IsEnoughSize(len)) {
		ExpandCapacity(len);
	}
	std::copy(data, data + len, data_.begin() + write_idx_);
	write_idx_ += len;
	// while (len--) {
	// 	data_[write_idx_++] = *data++;
	// }
}

void netlib::net::Buffer::Append(const void* data, int len) {
	Append(static_cast<const char*>(data), len);
}

void netlib::net::Buffer::Prepend(const char* data, int len) {
	assert(len <= PrependableBytes());
	read_idx_ -= len;
	std::copy(data, data + len, data_.begin() + read_idx_);
}

int netlib::net::Buffer::ReadFd(int fd, int* save_errno) {
	char extra_buf[65536];
	struct iovec vec[2];
	int writable = WritableBytes();
	vec[0].iov_base = &data_[write_idx_];
	vec[0].iov_len = writable;
	vec[1].iov_base = extra_buf;
	vec[1].iov_len = sizeof(extra_buf);
	int len = static_cast<int>(readv(fd, vec, 2));
	if (len < 0) {
		*save_errno = errno;
	} else if (len > writable) {
		write_idx_ = static_cast<int>(data_.size());
		Append(extra_buf, len - writable);
	} else {
		write_idx_ += len;
	}
	return len;
}

void netlib::net::Buffer::ExpandCapacity(int len) {
	if (len <= WritableBytes() + PrependableBytes() - kInitReadIndex) { // 往前移
		assert(read_idx_ > kInitReadIndex);
		int readable = ReadableBytes();
		std::move(data_.begin() + read_idx_, data_.begin() + write_idx_,
		          data_.begin() + kInitReadIndex);
		read_idx_ = kInitReadIndex;
		write_idx_ = read_idx_ + readable;
		assert(readable == ReadableBytes());
	} else { // 真扩容
		data_.resize(write_idx_ + len);
	}
	assert(len <= WritableBytes());
}
void netlib::net::Buffer::AppendCheckSum() {
	int32_t sum{};
	assert(WritableBytes() >= sizeof(sum));
	for (int i = read_idx_; i < write_idx_; i++) {
		sum += data_[i];
	}
	Append(&sum, sizeof(sum));
}
bool netlib::net::Buffer::CheckCheckSum(uint32_t start, uint32_t end) {
	int32_t rsum{};
	assert(read_idx_ + end + 4 <= data_.size());
	for (int i = read_idx_ + start; i < read_idx_ + end; i++) {
		rsum += data_[i];
	}
	int32_t sum = *reinterpret_cast<int32_t*>(&data_[read_idx_ + end]);
	return rsum == sum;
}
void netlib::net::Buffer::PrependSize() {
	uint32_t size = write_idx_ - read_idx_;
	Prepend(&size, sizeof(size));
}
